package com.phototodata;

import com.phototodata.service.DatabaseService;

import javax.swing.*;
import java.awt.*;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.io.File;
import java.io.IOException;
import java.nio.file.Files;
import java.sql.Connection;
import java.util.ArrayList;
import java.util.List;

/**
 * 照片数据转换工具主程序（Swing 版本）
 * 功能：将本地图片文件夹中的图片批量转换为 Base64 并存储到数据库
 *
 * @author PhotoToData
 * @version 1.0.0
 */
public class PhotoToDataApp extends JFrame {

    // 界面组件
    private JTextField imagePathField;
    private JButton browseButton;
    private JTextField dbUrlField;
    private JTextField dbUsernameField;
    private JPasswordField dbPasswordField;
    private JButton executeButton;
    private JTextArea logArea;
    private JScrollPane logScrollPane;

    public PhotoToDataApp() {
        initializeUI();
    }

    /**
     * 初始化界面
     */
    private void initializeUI() {
        setTitle("照片数据转换工具");
        setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
        setSize(950, 650);
        setLocationRelativeTo(null); // 居中显示
        setResizable(true);

        // 设置背景色（浅灰色背景，更现代）
        Color backgroundColor = new Color(245, 245, 250);
        Color panelColor = Color.WHITE;
        Color primaryColor = new Color(64, 158, 255); // 现代蓝色主题
        Color labelColor = new Color(51, 51, 51); // 深灰色文字
        
        // 设置窗口背景色
        getContentPane().setBackground(backgroundColor);

        // 创建主容器（垂直排列）
        JPanel mainPanel = new JPanel();
        mainPanel.setLayout(new BoxLayout(mainPanel, BoxLayout.Y_AXIS));
        mainPanel.setBorder(BorderFactory.createEmptyBorder(20, 20, 20, 20));
        mainPanel.setBackground(backgroundColor);

        // 第一行：目标本地图片路径输入区（固定高度）
        JPanel imagePathRow = createStyledRow(panelColor);
        imagePathRow.setPreferredSize(new Dimension(Integer.MAX_VALUE, 50));
        imagePathRow.setMaximumSize(new Dimension(Integer.MAX_VALUE, 50));
        imagePathRow.setMinimumSize(new Dimension(0, 50));
        
        JLabel imagePathLabel = createStyledLabel("本地图片文件夹：", labelColor);
        imagePathField = createStyledTextField(500);
        imagePathField.setToolTipText("例如：D:\\images 或 C:/images");
        
        browseButton = createStyledButton("浏览", primaryColor);
        browseButton.setPreferredSize(new Dimension(80, 32));
        browseButton.addActionListener(e -> browseImageFolder());
        
        imagePathRow.add(Box.createHorizontalStrut(5));
        imagePathRow.add(imagePathLabel);
        imagePathRow.add(Box.createHorizontalStrut(8));
        imagePathRow.add(imagePathField);
        imagePathRow.add(Box.createHorizontalStrut(8));
        imagePathRow.add(browseButton);
        imagePathRow.add(Box.createHorizontalStrut(5));

        // 第二行：数据库连接配置区（3 个输入框横向排列，固定高度）
        JPanel dbConfigRow = createStyledRow(panelColor);
        dbConfigRow.setPreferredSize(new Dimension(Integer.MAX_VALUE, 50));
        dbConfigRow.setMaximumSize(new Dimension(Integer.MAX_VALUE, 50));
        dbConfigRow.setMinimumSize(new Dimension(0, 50));
        
        JLabel dbUrlLabel = createStyledLabel("数据源 URL：", labelColor);
        dbUrlField = createStyledTextField(200);
        dbUrlField.setToolTipText("例如：jdbc:dm://localhost:5236/DAMENG");
        
        JLabel dbUsernameLabel = createStyledLabel("数据库账号：", labelColor);
        dbUsernameField = createStyledTextField(150);
        
        JLabel dbPasswordLabel = createStyledLabel("数据库密码：", labelColor);
        dbPasswordField = createStyledPasswordField(150);
        
        dbConfigRow.add(Box.createHorizontalStrut(5));
        dbConfigRow.add(dbUrlLabel);
        dbConfigRow.add(Box.createHorizontalStrut(8));
        dbConfigRow.add(dbUrlField);
        dbConfigRow.add(Box.createHorizontalStrut(15));
        dbConfigRow.add(dbUsernameLabel);
        dbConfigRow.add(Box.createHorizontalStrut(8));
        dbConfigRow.add(dbUsernameField);
        dbConfigRow.add(Box.createHorizontalStrut(15));
        dbConfigRow.add(dbPasswordLabel);
        dbConfigRow.add(Box.createHorizontalStrut(8));
        dbConfigRow.add(dbPasswordField);
        dbConfigRow.add(Box.createHorizontalStrut(5));

        // 第三行：执行按钮区（固定高度）
        JPanel executeRow = createStyledRow(panelColor);
        executeRow.setPreferredSize(new Dimension(Integer.MAX_VALUE, 50));
        executeRow.setMaximumSize(new Dimension(Integer.MAX_VALUE, 50));
        executeRow.setMinimumSize(new Dimension(0, 50));
        
        executeButton = createStyledButton("执行", primaryColor);
        executeButton.setPreferredSize(new Dimension(150, 38));
        executeButton.addActionListener(new ExecuteButtonListener());
        
        executeRow.add(Box.createHorizontalStrut(5));
        executeRow.add(Box.createHorizontalGlue());
        executeRow.add(executeButton);
        executeRow.add(Box.createHorizontalStrut(5));

        // 第四行：日志输出控制台
        JPanel logPanel = new JPanel();
        logPanel.setLayout(new BoxLayout(logPanel, BoxLayout.Y_AXIS));
        logPanel.setBackground(backgroundColor);
        logPanel.setBorder(BorderFactory.createEmptyBorder(5, 5, 5, 5));
        
        JLabel logLabel = createStyledLabel("操作日志", new Color(64, 158, 255));
        logLabel.setFont(new Font("Microsoft YaHei", Font.BOLD, 14));
        logLabel.setBorder(BorderFactory.createEmptyBorder(5, 5, 8, 5));
        
        logArea = new JTextArea();
        logArea.setEditable(false);
        logArea.setLineWrap(true); // 自动换行
        logArea.setWrapStyleWord(true);
        // 使用支持中文的字体，优先尝试等宽字体
        Font logFont = getChineseFont(12);
        logArea.setFont(logFont);
        logArea.setBackground(new Color(250, 250, 255));
        logArea.setForeground(new Color(51, 51, 51));
        logArea.setBorder(BorderFactory.createCompoundBorder(
            BorderFactory.createLineBorder(new Color(220, 220, 230), 1),
            BorderFactory.createEmptyBorder(8, 8, 8, 8)
        ));
        
        logScrollPane = new JScrollPane(logArea);
        logScrollPane.setPreferredSize(new Dimension(910, 320));
        logScrollPane.setVerticalScrollBarPolicy(JScrollPane.VERTICAL_SCROLLBAR_AS_NEEDED);
        logScrollPane.setBorder(BorderFactory.createEmptyBorder());
        logScrollPane.getViewport().setBackground(logArea.getBackground());
        
        logPanel.add(logLabel);
        logPanel.add(logScrollPane);

        // 组装界面
        mainPanel.add(imagePathRow);
        mainPanel.add(Box.createVerticalStrut(12)); // 间距 12px
        mainPanel.add(dbConfigRow);
        mainPanel.add(Box.createVerticalStrut(12)); // 间距 12px
        mainPanel.add(executeRow);
        mainPanel.add(Box.createVerticalStrut(15)); // 间距 15px
        mainPanel.add(logPanel);

        add(mainPanel);

        // 初始化日志
        appendLog("程序启动成功，请配置参数后点击执行按钮。");
    }

    /**
     * 创建美化的行容器
     */
    private JPanel createStyledRow(Color backgroundColor) {
        JPanel row = new JPanel(new FlowLayout(FlowLayout.LEFT, 0, 0));
        row.setBackground(backgroundColor);
        row.setBorder(BorderFactory.createCompoundBorder(
            BorderFactory.createLineBorder(new Color(220, 220, 230), 1),
            BorderFactory.createEmptyBorder(10, 10, 10, 10)
        ));
        return row;
    }

    /**
     * 创建美化的标签
     */
    private JLabel createStyledLabel(String text, Color textColor) {
        JLabel label = new JLabel(text);
        label.setFont(new Font("Microsoft YaHei", Font.PLAIN, 13));
        label.setForeground(textColor);
        return label;
    }

    /**
     * 创建美化的文本输入框
     */
    private JTextField createStyledTextField(int width) {
        JTextField field = new JTextField();
        field.setPreferredSize(new Dimension(width, 32));
        field.setFont(new Font("Microsoft YaHei", Font.PLAIN, 13));
        field.setBorder(BorderFactory.createCompoundBorder(
            BorderFactory.createLineBorder(new Color(200, 200, 210), 1),
            BorderFactory.createEmptyBorder(5, 8, 5, 8)
        ));
        field.setBackground(Color.WHITE);
        field.setForeground(new Color(51, 51, 51));
        // 添加焦点效果
        field.addFocusListener(new java.awt.event.FocusAdapter() {
            public void focusGained(java.awt.event.FocusEvent evt) {
                field.setBorder(BorderFactory.createCompoundBorder(
                    BorderFactory.createLineBorder(new Color(64, 158, 255), 2),
                    BorderFactory.createEmptyBorder(4, 7, 4, 7)
                ));
            }
            public void focusLost(java.awt.event.FocusEvent evt) {
                field.setBorder(BorderFactory.createCompoundBorder(
                    BorderFactory.createLineBorder(new Color(200, 200, 210), 1),
                    BorderFactory.createEmptyBorder(5, 8, 5, 8)
                ));
            }
        });
        return field;
    }

    /**
     * 创建美化的密码输入框
     */
    private JPasswordField createStyledPasswordField(int width) {
        JPasswordField field = new JPasswordField();
        field.setPreferredSize(new Dimension(width, 32));
        field.setFont(new Font("Microsoft YaHei", Font.PLAIN, 13));
        field.setBorder(BorderFactory.createCompoundBorder(
            BorderFactory.createLineBorder(new Color(200, 200, 210), 1),
            BorderFactory.createEmptyBorder(5, 8, 5, 8)
        ));
        field.setBackground(Color.WHITE);
        field.setForeground(new Color(51, 51, 51));
        // 添加焦点效果
        field.addFocusListener(new java.awt.event.FocusAdapter() {
            public void focusGained(java.awt.event.FocusEvent evt) {
                field.setBorder(BorderFactory.createCompoundBorder(
                    BorderFactory.createLineBorder(new Color(64, 158, 255), 2),
                    BorderFactory.createEmptyBorder(4, 7, 4, 7)
                ));
            }
            public void focusLost(java.awt.event.FocusEvent evt) {
                field.setBorder(BorderFactory.createCompoundBorder(
                    BorderFactory.createLineBorder(new Color(200, 200, 210), 1),
                    BorderFactory.createEmptyBorder(5, 8, 5, 8)
                ));
            }
        });
        return field;
    }

    /**
     * 创建美化的按钮
     */
    private JButton createStyledButton(String text, Color backgroundColor) {
        final Color[] currentBgColor = {backgroundColor}; // 使用数组来存储可变引用
        
        JButton button = new JButton(text) {
            @Override
            protected void paintComponent(Graphics g) {
                // 自定义绘制背景色
                Graphics2D g2d = (Graphics2D) g.create();
                g2d.setRenderingHint(java.awt.RenderingHints.KEY_ANTIALIASING, 
                    java.awt.RenderingHints.VALUE_ANTIALIAS_ON);
                
                // 绘制圆角矩形背景
                g2d.setColor(isEnabled() ? currentBgColor[0] : new Color(200, 200, 210));
                g2d.fillRoundRect(0, 0, getWidth(), getHeight(), 5, 5);
                
                // 绘制文字
                g2d.setColor(getForeground());
                FontMetrics fm = g2d.getFontMetrics(getFont());
                int textX = (getWidth() - fm.stringWidth(getText())) / 2;
                int textY = (getHeight() + fm.getAscent() - fm.getDescent()) / 2;
                g2d.setFont(getFont());
                g2d.drawString(getText(), textX, textY);
                
                g2d.dispose();
            }
        };
        
        button.setFont(new Font("Microsoft YaHei", Font.BOLD, 13));
        button.setBackground(backgroundColor);
        button.setForeground(Color.WHITE);
        button.setFocusPainted(false);
        button.setBorderPainted(false);
        button.setContentAreaFilled(false); // 禁用默认填充，使用自定义绘制
        button.setOpaque(false); // 设置为不透明但使用自定义绘制
        button.setBorder(BorderFactory.createEmptyBorder(10, 20, 10, 20));
        button.setCursor(new Cursor(Cursor.HAND_CURSOR));
        
        // 添加悬停效果
        button.addMouseListener(new java.awt.event.MouseAdapter() {
            public void mouseEntered(java.awt.event.MouseEvent evt) {
                if (button.isEnabled()) {
                    currentBgColor[0] = new Color(backgroundColor.getRed(), backgroundColor.getGreen(), 
                        Math.min(255, backgroundColor.getBlue() + 20));
                    button.repaint();
                }
            }
            public void mouseExited(java.awt.event.MouseEvent evt) {
                if (button.isEnabled()) {
                    currentBgColor[0] = backgroundColor;
                    button.repaint();
                }
            }
            public void mousePressed(java.awt.event.MouseEvent evt) {
                if (button.isEnabled()) {
                    currentBgColor[0] = new Color(backgroundColor.getRed(), backgroundColor.getGreen(), 
                        Math.max(0, backgroundColor.getBlue() - 20));
                    button.repaint();
                }
            }
            public void mouseReleased(java.awt.event.MouseEvent evt) {
                if (button.isEnabled()) {
                    currentBgColor[0] = backgroundColor;
                    button.repaint();
                }
            }
        });
        
        // 禁用状态样式处理
        button.addPropertyChangeListener("enabled", evt -> {
            if (!button.isEnabled()) {
                currentBgColor[0] = new Color(200, 200, 210);
                button.setForeground(new Color(150, 150, 160));
            } else {
                currentBgColor[0] = backgroundColor;
                button.setForeground(Color.WHITE);
            }
            button.repaint();
        });
        
        return button;
    }

    /**
     * 执行按钮监听器
     */
    private class ExecuteButtonListener implements ActionListener {
        @Override
        public void actionPerformed(ActionEvent e) {
            // 在新线程中执行，避免阻塞 UI
            SwingUtilities.invokeLater(() -> {
                executeButton.setEnabled(false);
                new Thread(() -> executeTask()).start();
            });
        }
    }

    /**
     * 浏览图片文件夹
     */
    private void browseImageFolder() {
        JFileChooser fileChooser = new JFileChooser();
        fileChooser.setDialogTitle("选择图片文件夹");
        fileChooser.setFileSelectionMode(JFileChooser.DIRECTORIES_ONLY);
        
        // 设置默认目录
        fileChooser.setCurrentDirectory(new File(System.getProperty("user.home")));
        
        int result = fileChooser.showOpenDialog(this);
        if (result == JFileChooser.APPROVE_OPTION) {
            File selectedFolder = fileChooser.getSelectedFile();
            imagePathField.setText(selectedFolder.getAbsolutePath());
        }
    }

    /**
     * 获取文件夹中的所有图片文件
     */
    private List<File> getImageFiles(File folder) {
        List<File> imageFiles = new ArrayList<>();
        if (folder == null || !folder.exists() || !folder.isDirectory()) {
            return imageFiles;
        }
        
        File[] files = folder.listFiles();
        if (files == null) {
            return imageFiles;
        }
        
        // 支持的图片格式（不区分大小写）
        String[] imageExtensions = {".jpg", ".jpeg", ".png", ".gif", ".bmp"};
        
        for (File file : files) {
            if (file.isFile()) {
                String fileName = file.getName().toLowerCase();
                for (String ext : imageExtensions) {
                    if (fileName.endsWith(ext)) {
                        imageFiles.add(file);
                        break;
                    }
                }
            }
        }
        
        return imageFiles;
    }

    /**
     * 执行核心业务逻辑
     */
    private void executeTask() {
        SwingUtilities.invokeLater(() -> appendLog("========== 开始执行任务 =========="));

        try {
            // 1. 参数校验
            if (!validateParameters()) {
                SwingUtilities.invokeLater(() -> executeButton.setEnabled(true));
                return;
            }

            String folderPath = imagePathField.getText().trim();
            String dbUrl = dbUrlField.getText().trim();
            String dbUsername = dbUsernameField.getText().trim();
            String dbPassword = new String(dbPasswordField.getPassword());

            // 2. 检查文件夹是否存在
            File folder = new File(folderPath);
            if (!folder.exists()) {
                SwingUtilities.invokeLater(() -> {
                    appendLog("✗ 错误：文件夹不存在：" + folderPath);
                    executeButton.setEnabled(true);
                });
                return;
            }

            if (!folder.isDirectory()) {
                SwingUtilities.invokeLater(() -> {
                    appendLog("✗ 错误：指定的路径不是文件夹：" + folderPath);
                    executeButton.setEnabled(true);
                });
                return;
            }

            SwingUtilities.invokeLater(() -> appendLog("图片文件夹：" + folderPath));

            // 3. 获取文件夹中的所有图片文件
            SwingUtilities.invokeLater(() -> appendLog("正在扫描图片文件..."));
            List<File> imageFiles = getImageFiles(folder);
            
            if (imageFiles.isEmpty()) {
                SwingUtilities.invokeLater(() -> {
                    appendLog("⚠ 警告：文件夹中没有找到图片文件");
                    appendLog("支持的格式：jpg, jpeg, png, gif, bmp");
                    executeButton.setEnabled(true);
                });
                return;
            }

            SwingUtilities.invokeLater(() -> 
                appendLog("✓ 找到 " + imageFiles.size() + " 个图片文件"));

            // 4. 数据库连接
            DatabaseService dbService = new DatabaseService(dbUrl, dbUsername, dbPassword);
            Connection connection = null;
            try {
                connection = dbService.getConnection();
                SwingUtilities.invokeLater(() -> appendLog("✓ 数据库连接成功"));
            } catch (Exception e) {
                SwingUtilities.invokeLater(() -> {
                    appendLog("✗ 数据库连接失败：" + e.getMessage());
                    executeButton.setEnabled(true);
                });
                e.printStackTrace();
                return;
            }

            // 5. 遍历处理每个图片文件
            final int[] successCount = {0};
            final int[] failCount = {0};
            final int[] totalSize = {0};

            try {
                for (int i = 0; i < imageFiles.size(); i++) {
                    File imageFile = imageFiles.get(i);
                    String fileName = imageFile.getName();
                    final int currentIndex = i + 1;

                    SwingUtilities.invokeLater(() -> 
                        appendLog("[" + currentIndex + "/" + imageFiles.size() + "] 正在处理：" + fileName));

                    try {
                        // 5.1 读取图片文件
                        byte[] imageBytes = Files.readAllBytes(imageFile.toPath());
                        totalSize[0] += imageBytes.length;

                        // 5.2 转换为 Base64（使用 Java8 原生 Base64）
                        // 根据文件扩展名获取 MIME 类型并添加 data URI 前缀
                        String mimeType = getImageMimeType(fileName);
                        String base64Data = java.util.Base64.getEncoder().encodeToString(imageBytes);
                        String base64String = "data:" + mimeType + ";base64," + base64Data;

                        // 5.3 获取文件名（不含扩展名）用于存储到数据库的 a00 字段
                        String fileNameWithoutExtension = removeFileExtension(fileName);

                        // 5.4 执行 UPSERT 操作（存储到数据库）
                        dbService.upsertImageData(connection, fileNameWithoutExtension, base64String);
                        
                        SwingUtilities.invokeLater(() -> {
                            appendLog("  ✓ 文件 " + fileName + " 处理成功（" + 
                                imageBytes.length + " 字节）");
                        });
                        successCount[0]++;

                    } catch (IOException e) {
                        SwingUtilities.invokeLater(() -> {
                            appendLog("  ✗ 文件 " + fileName + " 读取失败：" + e.getMessage());
                        });
                        failCount[0]++;
                        e.printStackTrace();
                    } catch (Exception e) {
                        SwingUtilities.invokeLater(() -> {
                            appendLog("  ✗ 文件 " + fileName + " 存储失败：" + e.getMessage());
                        });
                        failCount[0]++;
                        e.printStackTrace();
                    }
                }

                // 6. 输出统计信息
                SwingUtilities.invokeLater(() -> {
                    appendLog("========== 任务执行完成 ==========");
                    appendLog("总计：" + imageFiles.size() + " 个文件");
                    appendLog("成功：" + successCount[0] + " 个");
                    appendLog("失败：" + failCount[0] + " 个");
                    appendLog("总大小：" + totalSize[0] + " 字节（" + 
                        String.format("%.2f", totalSize[0] / 1024.0 / 1024.0) + " MB）");
                    executeButton.setEnabled(true);
                });

            } finally {
                // 7. 资源释放
                if (connection != null) {
                    try {
                        dbService.closeConnection(connection);
                        SwingUtilities.invokeLater(() -> appendLog("✓ 数据库连接已关闭"));
                    } catch (Exception e) {
                        SwingUtilities.invokeLater(() -> 
                            appendLog("⚠ 关闭数据库连接时出现异常：" + e.getMessage()));
                    }
                }
            }

        } catch (Exception e) {
            SwingUtilities.invokeLater(() -> {
                appendLog("✗ 执行过程中发生异常：" + e.getMessage());
                executeButton.setEnabled(true);
            });
            e.printStackTrace();
        }
    }

    /**
     * 参数校验
     */
    private boolean validateParameters() {
        if (imagePathField.getText() == null || imagePathField.getText().trim().isEmpty()) {
            SwingUtilities.invokeLater(() -> appendLog("✗ 错误：本地图片文件夹不能为空"));
            return false;
        }

        if (dbUrlField.getText() == null || dbUrlField.getText().trim().isEmpty()) {
            SwingUtilities.invokeLater(() -> appendLog("✗ 错误：数据源 URL 不能为空"));
            return false;
        }

        if (dbUsernameField.getText() == null || dbUsernameField.getText().trim().isEmpty()) {
            SwingUtilities.invokeLater(() -> appendLog("✗ 错误：数据库账号不能为空"));
            return false;
        }

        return true;
    }

    /**
     * 移除文件扩展名，返回不含后缀的文件名
     *
     * @param fileName 完整文件名（包含扩展名）
     * @return 不含扩展名的文件名
     */
    private String removeFileExtension(String fileName) {
        if (fileName == null || fileName.isEmpty()) {
            return fileName;
        }
        
        int lastDotIndex = fileName.lastIndexOf('.');
        if (lastDotIndex > 0) {
            return fileName.substring(0, lastDotIndex);
        }
        
        // 如果没有找到点号，返回原文件名
        return fileName;
    }

    /**
     * 根据文件名获取图片的 MIME 类型
     *
     * @param fileName 文件名
     * @return MIME 类型字符串
     */
    private String getImageMimeType(String fileName) {
        if (fileName == null || fileName.isEmpty()) {
            return "image/png"; // 默认返回 png
        }
        
        String lowerFileName = fileName.toLowerCase();
        
        if (lowerFileName.endsWith(".png")) {
            return "image/png";
        } else if (lowerFileName.endsWith(".jpg") || lowerFileName.endsWith(".jpeg")) {
            return "image/jpeg";
        } else if (lowerFileName.endsWith(".gif")) {
            return "image/gif";
        } else if (lowerFileName.endsWith(".bmp")) {
            return "image/bmp";
        } else if (lowerFileName.endsWith(".webp")) {
            return "image/webp";
        } else if (lowerFileName.endsWith(".svg")) {
            return "image/svg+xml";
        } else {
            // 默认返回 png
            return "image/png";
        }
    }

    /**
     * 获取支持中文的字体
     */
    private Font getChineseFont(int size) {
        // 优先尝试的字体列表（支持中文）
        String[] fontNames = {
            "Microsoft YaHei",
            "SimSun",
            "SimHei",
            "KaiTi",
            "FangSong",
            "宋体",
            "黑体",
            "Dialog",
            "SansSerif"
        };
        
        GraphicsEnvironment ge = GraphicsEnvironment.getLocalGraphicsEnvironment();
        String[] availableFonts = ge.getAvailableFontFamilyNames();
        
        // 尝试找到第一个可用的字体
        for (String fontName : fontNames) {
            for (String available : availableFonts) {
                if (available.equals(fontName)) {
                    return new Font(fontName, Font.PLAIN, size);
                }
            }
        }
        
        // 如果没有找到，使用默认的 Dialog 字体（通常支持中文）
        return new Font(Font.DIALOG, Font.PLAIN, size);
    }

    /**
     * 追加日志到控制台
     */
    private void appendLog(String message) {
        String timestamp = java.time.LocalDateTime.now().format(
                java.time.format.DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss"));
        logArea.append("[" + timestamp + "] " + message + "\n");
        // 自动滚动到底部
        SwingUtilities.invokeLater(() -> {
            logArea.setCaretPosition(logArea.getDocument().getLength());
        });
    }

    /**
     * 主程序入口
     */
    public static void main(String[] args) {
        // 设置系统外观
        try {
            UIManager.setLookAndFeel(UIManager.getSystemLookAndFeelClassName());
        } catch (Exception e) {
            e.printStackTrace();
        }

        // 创建并显示界面
        SwingUtilities.invokeLater(() -> {
            PhotoToDataApp app = new PhotoToDataApp();
            app.setVisible(true);
        });
    }
}

